#include "raytrace_script_object.h"

#include <ppapi/cpp/var.h>

#include <cstdio>

#include "raytrace.h"

namespace raytrace {

// TODO: use exceptions to return sensible errors

RaytraceScriptObject::RaytraceScriptObject(Raytrace* app_instance)
  : pp::deprecated::ScriptableObject(),
    app_instance_(app_instance) {
}

void RaytraceScriptObject::RegisterMethod(const std::string& method_name,
                                          MethodFunc method) {
  method_map_[method_name] = method;
  printf("Registered method: %s\n", method_name.c_str());
}

void RaytraceScriptObject::RegisterProperty(const std::string& property_name,
                                            GetPropertyFunc get_property,
                                            SetPropertyFunc set_property) {
  property_map_[property_name] = std::make_pair(get_property, set_property);
}

bool RaytraceScriptObject::HasMethod(const pp::Var& method,
                                     pp::Var* exception) {
  if (!method.is_string())
    return false;

  std::string method_name = method.AsString();
//  printf("Checking for method: %s\n", method_name.c_str());
  return (method_map_.find(method_name) != method_map_.end());
}

bool RaytraceScriptObject::HasProperty(const pp::Var& property,
                                       pp::Var* exception) {
  if (!property.is_string())
    return false;

  std::string property_name = property.AsString();
//  printf("Checking for property: %s\n", property_name.c_str());
  return (property_map_.find(property_name) != property_map_.end());
}

pp::Var RaytraceScriptObject::Call(const pp::Var& method_var,
                                   const std::vector<pp::Var>& args,
                                   pp::Var* exception) {
  pp::Var return_var;
  if (!method_var.is_string())
    return return_var;
  if (app_instance_ == NULL)
    return return_var;

  std::string method_name = method_var.AsString();
  std::map<std::string, MethodFunc>::iterator method_it =
    method_map_.find(method_name);
  if (method_it == method_map_.end())
    return return_var;

//  printf("Calling method: %s\n", method_name.c_str());
  MethodFunc method = method_it->second;
  return_var = (app_instance_->*method)();
  return return_var;
}

void RaytraceScriptObject::SetProperty(const pp::Var& property,
                                       const pp::Var& value,
                                       pp::Var* exception) {
  if (!property.is_string()) {
    if (exception)
      *exception = std::string("SetProperty: property is not a string");
    return;
  }
  if (app_instance_ == NULL) {
    if (exception)
      *exception = std::string("SetProperty: app_instance_ is not set");
    return;
  }
  
  std::string property_name = property.AsString();
  std::map<std::string,
           std::pair<GetPropertyFunc, SetPropertyFunc> >::iterator prop_it =
              property_map_.find(property_name);
  if (prop_it == property_map_.end()) {
    if (exception)
      *exception = std::string("SetProperty: Failed to find property ") +
                               property_name;
    return;
  }

  printf("Setting property %s\n", property_name.c_str());
  SetPropertyFunc set_property = prop_it->second.second;
  bool success = (app_instance_->*set_property)(value);
  if (!success && exception)
    *exception = std::string("Failed to set property ") + property_name;
}

pp::Var RaytraceScriptObject::GetProperty(const pp::Var& property,
                                          pp::Var* exception) {
  if (!property.is_string()) {
    if (exception)
      *exception = std::string("GetProperty: property is not a string");
    return pp::Var();
  }
  if (app_instance_ == NULL) {
    if (exception)
      *exception = std::string("GetProperty: app_instance_ is not set");
    return pp::Var();
  }

  std::string property_name = property.AsString();
  std::map<std::string,
           std::pair<GetPropertyFunc, SetPropertyFunc> >::iterator prop_it =
             property_map_.find(property_name);
  if (prop_it == property_map_.end()) {
    if (exception)
      *exception = std::string("GetProperty: Failed to find property ") +
                               property_name;
    return pp::Var();
  }

  pp::Var return_var;

  printf("Getting property %s\n", property_name.c_str());
  GetPropertyFunc get_property = prop_it->second.first;
  bool success = (app_instance_->*get_property)(return_var);
  if (success)
    return return_var;

  if (exception)
    *exception = std::string("Failed to get property ") + property_name;
  return pp::Var();
}
}

